//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// C# Port port by: Lars Brubaker
//                  larsbrubaker@gmail.com
// Copyright (C) 2007
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
using System;
using System.Collections.Generic;

using AGG.Image;
using AGG.VertexSource;
using AGG.Transform;

namespace AGG
{
    public interface IStyleHandler
    {
        bool is_solid(int style);
        RGBA_Bytes color(int style);
        void generate_span(RGBA_Bytes[] span, int spanIndex, int x, int y, int len, int style);
    };

    public abstract class Graphics2D
    {
        const int cover_full = 255;
        protected IImageByte m_DestImage;
        protected IImageFloat m_DestImageFloat;
        protected gsv_text TextPath;
        protected conv_stroke StrockedText;
        protected Stack<Affine> m_AffineTransformStack = new Stack<Affine>();
        protected ScanlineRasterizer m_Rasterizer;

        public enum BlendMode { DO_NOT_SET_DEST, USE_IMAGE_BLENDER, FORCE_SOURC_ONE_DEST_ONE_MINUS_ALPHA_BLENDER };

        public Graphics2D()
        {
            TextPath = new gsv_text();
            StrockedText = new conv_stroke(TextPath);
            m_AffineTransformStack.Push(Affine.NewIdentity());
        }

        public Graphics2D(IImageByte destImage, ScanlineRasterizer rasterizer)
            : this()
        {
            Initialize(destImage, rasterizer);
        }

        public void Initialize(IImageByte destImage, ScanlineRasterizer rasterizer)
        {
            m_DestImage = destImage;
            m_DestImageFloat = null;
            m_Rasterizer = rasterizer;
        }

        public void Initialize(IImageFloat destImage, ScanlineRasterizer rasterizer)
        {
            m_DestImage = null;
            m_DestImageFloat = destImage;
            m_Rasterizer = rasterizer;
        }

        public Affine PopTransform()
        {
            if (m_AffineTransformStack.Count == 1)
            {
                throw new System.Exception("You cannot remove the last transform from the stack.");
            }

            return m_AffineTransformStack.Pop();
        }

        public void PushTransform()
        {
            if (m_AffineTransformStack.Count > 1000)
            {
                throw new System.Exception("You seem to be leaking transforms.  You should be poping some of them at some point.");
            }

            m_AffineTransformStack.Push(m_AffineTransformStack.Peek());
        }

        public Affine GetTransform()
        {
            return m_AffineTransformStack.Peek();
        }

        public void SetTransform(Affine value)
        {
            m_AffineTransformStack.Pop();
            m_AffineTransformStack.Push(value);
        }

        public ScanlineRasterizer Rasterizer
        {
            get { return m_Rasterizer; }
        }

        public abstract IScanlineCache ScanlineCache
        {
            get;
            set;
        }

        public IImageByte DestImage
        {
            get
            {
                return m_DestImage;
            }
        }

        public IImageFloat DestImageFloat
        {
            get
            {
                return m_DestImageFloat;
            }
        }

        public abstract void Render(IVertexSource vertexSource, int pathIndexToRender, RGBA_Bytes colorBytes);

        public void Render(IImageByte imageSource, double x, double y)
        {
            Render(imageSource, x, y, 0, 1, 1, new RGBA_Bytes(255, 255, 255), BlendMode.FORCE_SOURC_ONE_DEST_ONE_MINUS_ALPHA_BLENDER);
        }

        public void Render(IImageByte imageSource, double x, double y, RGBA_Bytes color)
        {
            Render(imageSource, x, y, 0, 1, 1, color, BlendMode.FORCE_SOURC_ONE_DEST_ONE_MINUS_ALPHA_BLENDER);
        }

        public void Render(IImageByte imageSource, double x, double y, RGBA_Bytes color, BlendMode renderingMode)
        {
            Render(imageSource, x, y, 0, 1, 1, color, renderingMode);
        }

        public abstract void Render(IImageByte imageSource, 
            double x, double y, 
            double angleDegrees, 
            double scaleX, double ScaleY,
            RGBA_Bytes color, 
            BlendMode renderingMode);

        public abstract void Render(IImageFloat imageSource,
            double x, double y,
            double angleDegrees,
            double scaleX, double ScaleY,
            RGBA_Floats color,
            BlendMode renderingMode);

        public void Render(IVertexSource vertexSource, RGBA_Bytes[] colorArray, int[] pathIdArray, int numPaths)
        {
            for (int i = 0; i < numPaths; i++)
            {
                Render(vertexSource, pathIdArray[i], colorArray[i]);
            }
        }

        public void Render(IVertexSource vertexSource, RGBA_Bytes color)
        {
            Render(vertexSource, 0, color);
        }

        public void Render(IVertexSource vertexSource, double x, double y, RGBA_Bytes color)
        {
            Render(new VertexSourceApplyTransform(vertexSource, Affine.NewTranslation(x, y)), 0, color);
        }

        public void Render(IVertexSource vertexSource, Vector2D position, RGBA_Bytes color)
        {
            Render(new VertexSourceApplyTransform(vertexSource, Affine.NewTranslation(position.x, position.y)), 0, color);
        }

        public abstract void Clear(IColorType color);

        public class StringFormat
        {
            public enum Justification { Left, Center, Right };
            public enum Baseline { BoundsTop, BoundsCenter, TextCenter, Text, BoundsBottom };

            public Justification justification = Justification.Left;
            public Baseline baseline = Baseline.Text;
            public double size = 10;
            public RGBA_Bytes color = RGBA_Bytes.Black;
        }

        public void DrawString(string Text, double x, double y)
        {
            DrawString(Text, x, y, new StringFormat());
        }

        public void DrawString(string Text, double x, double y, StringFormat.Baseline baseline)
        {
            StringFormat drawStringParams = new StringFormat();
            drawStringParams.baseline = baseline;
            DrawString(Text, x, y, drawStringParams);
        }

        public void DrawString(string Text, double x, double y, StringFormat drawStringParams)
        {
            TextPath.SetFontSize(10);
            TextPath.text(Text);
            switch (drawStringParams.justification)
            {
                case StringFormat.Justification.Left:
                    break;

                default:
                    throw new NotImplementedException();
            }
            switch (drawStringParams.baseline)
            {
                case StringFormat.Baseline.Text:
                    break;

                case StringFormat.Baseline.BoundsTop:
                    y -= TextPath.AscenderHeight;
                    break;

                default:
                    throw new NotImplementedException();
            }
            TextPath.start_point(x, y);
            Render(StrockedText, drawStringParams.color);
        }

        public void Circle(Vector2D origin, double radius, RGBA_Bytes color)
        {
            Ellipse elipse = new Ellipse(origin.x, origin.y, radius, radius);
            Render(elipse, color);
        }

        public void Line(Vector2D start, Vector2D end, RGBA_Bytes color)
        {
            Line(start.x, start.y, end.x, end.y, color);
        }

        public void Line(double x1, double y1, double x2, double y2, RGBA_Bytes color)
        {
            PathStorage m_LinesToDraw = new PathStorage();
            m_LinesToDraw.remove_all();
            m_LinesToDraw.move_to(x1, y1);
            m_LinesToDraw.line_to(x2, y2);
            conv_stroke StrockedLineToDraw = new conv_stroke(m_LinesToDraw);
            Render(StrockedLineToDraw, color);
        }

        public abstract void SetClippingRect(rect_d rect_d);

        public void Rect(double left, double bottom, double right, double top, RGBA_Bytes color)
        {
            Line(left, bottom, right, bottom, color);
            Line(right, bottom, right, top, color);
            Line(right, top, left, top, color);
            Line(left, top, left, bottom, color);
        }

        public void Rect(rect_d rect, RGBA_Bytes color)
        {
            Rect(rect.Left, rect.Bottom, rect.Right, rect.Top, color);
        }

        public void FillRectangle(rect_d rect, RGBA_Doubles color)
        {
            throw new NotImplementedException();
        }

        public void FillRectangle(rect_i viewport, RGBA_Doubles fillColor)
        {
            FillRectangle(viewport.Left, viewport.Bottom, viewport.Right, viewport.Top, fillColor);
        }

        public void FillRectangle(int left, int bottom, int right, int top, RGBA_Doubles fillColor)
        {
            if (right < left || top < bottom)
            {
                throw new ArgumentException();
            }
            RoundedRect rect = new RoundedRect(left, bottom, right, top, 0);
            Render(rect, fillColor.GetAsRGBA_Bytes());
        }
    }
}
